/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package main.CrossValidation;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import main.KBProcess.KnowledgeBase;
import main.KBProcess.KnowledgeBaseDocument;
import main.NER.Trie.KBTrie;
import main.NER.Trie.NER_Candidate;
import main.Ontology.ElementOntologyFactory;
import main.Ontology.ElementOntologySet;
import main.Weighting.EvaluationParams;
import main.Weighting.WeightingMethod;
import main.initiate.CandidateExtractor_Threaded;

/**
 *
 * @author Isaac Osesina
 */
public class EvaluateFolds implements Serializable, Cloneable{
    
    private int foldCount;
    private List<KnowledgeBase> kbList;
    private ElementOntologyFactory elementOntologyFactory;

    public EvaluateFolds(ElementOntologyFactory elementOntologyFactory, List<KnowledgeBase> kbList) {
        this.elementOntologyFactory = elementOntologyFactory;
        this.kbList = kbList;
        foldCount = 10;
    }

    public EvaluateFolds(ElementOntologyFactory elementOntologyFactory, KnowledgeBase kb) {
        this.elementOntologyFactory = elementOntologyFactory;
        this.kbList = kbList==null ? new ArrayList<KnowledgeBase>():kbList;
        kbList.add(kb);
        foldCount = 10;
    }

    public EvaluateFolds(ElementOntologyFactory elementOntologyFactory, KBTrie trie) {
        this.elementOntologyFactory = elementOntologyFactory;
        this.kbList = trie.getKbList();
        foldCount = 10;
    }

    public EvaluateFolds(ElementOntologyFactory elementOntologyFactory, List<KnowledgeBase> kbList, int foldCount) {
        this.elementOntologyFactory = elementOntologyFactory;
        this.kbList = kbList;
        this.foldCount = foldCount;
    }


    public void addKB(KnowledgeBase kb) {
        this.kbList = kbList==null ? new ArrayList<KnowledgeBase>():kbList;
        kbList.add(kb);
    }

    public static List<KnowledgeBaseDocument> getKBDocuments(List<KnowledgeBase> kbList) {
        List<KnowledgeBaseDocument> fragmentList = null;
        if (kbList != null) {
            fragmentList = new ArrayList<KnowledgeBaseDocument>();
            for (KnowledgeBase kb : kbList) {
                if (kb != null) {
                    if (kb.getFragmentList() != null) {
                        fragmentList.addAll(kb.getKBDocs());
                    }
                }
            }
        }
        return fragmentList;
    }

     public List<KnowledgeBaseDocument> getKBDocuments() {
         return getKBDocuments(kbList);
     }

    public Map<ElementOntologySet, String> evaluateCrossValidation() {
        if(kbList!=null) {
            List<KnowledgeBaseDocument> docs = getKBDocuments(kbList);
            List<List<KnowledgeBaseDocument>> classifyDocFolds = getFolds_Docs(docs);
            List<Map<String, Map<ElementOntologySet, EvaluationParams>>> doCrossValidation = doCrossValidation(classifyDocFolds);
            return WorkSpace.getBestWeightingMethods(doCrossValidation);
        }
        return null;
    }


    private List<List<KnowledgeBaseDocument>> getFolds_Docs(List<KnowledgeBaseDocument> docs) {
        List<List<KnowledgeBaseDocument>> out = new ArrayList<List<KnowledgeBaseDocument>>();
        List<KnowledgeBaseDocument> workingInDocID = new ArrayList<KnowledgeBaseDocument>(docs);
        Collections.shuffle(workingInDocID);
        int groupSize = workingInDocID.size()/foldCount;
        groupSize = groupSize > 0 ? groupSize:workingInDocID.size();
        for( int i=0; i<workingInDocID.size();  i=i+groupSize ) {
            out.add( new ArrayList<KnowledgeBaseDocument>(workingInDocID.subList(i, i+groupSize)) );
        }
        return out;
    }

    private List<Map<String, Map<ElementOntologySet, EvaluationParams>>> doCrossValidation(List<List<KnowledgeBaseDocument>> docID_Test) {
        List<Map<String, Map<ElementOntologySet, EvaluationParams>>> result = new ArrayList<Map<String, Map<ElementOntologySet, EvaluationParams>>>();
        for(int fold=0; fold<docID_Test.size(); fold++) {
            KBTrie kbTrie = new KBTrie(elementOntologyFactory);
            List<KnowledgeBaseDocument> testDocs = docID_Test.get(fold);
            if(testDocs == null)
                continue;
            for(KnowledgeBase kb:kbList) {
                List<KnowledgeBaseDocument> tmp = new ArrayList<KnowledgeBaseDocument>(testDocs);
                tmp.removeAll(kb.getFragmentList());
                if( tmp.size() < testDocs.size() ) {
                    KnowledgeBase kb_clone = kb.clone();
                    kb_clone.removeKBDocs(testDocs);
                    kbTrie.addKBToModel(kb_clone);
                }
                else {
                    kbTrie.addKBToModel(kb);
                }
            }
            Map<String, Map<ElementOntologySet, EvaluationParams>> weightMethod_evals = extractCandidates(kbTrie, testDocs);
            if(weightMethod_evals != null) {
                result.add(weightMethod_evals);
            }
        }
        return result;
    }

    
    private Map<String, Map<ElementOntologySet, EvaluationParams>> extractCandidates(KBTrie kbTrie, List<KnowledgeBaseDocument> testDocs) {
        if(kbTrie==null || testDocs==null)
            return null;
        CandidateExtractor_Threaded extractor = new CandidateExtractor_Threaded(kbTrie);
        for(String weightMethod:WeightingMethod.WeightingMethodsNameList) {
            extractor.run();
            for(KnowledgeBaseDocument testDoc:testDocs) {
                if(weightMethod==null || testDoc==null)
                    continue;
                extractor.submitJob(testDoc.getDocText(), weightMethod);
            }
        }
        Map<String, List<NER_Candidate>> weightMethod_Candidate = new HashMap<String, List<NER_Candidate>>();
        while(extractor.activeThreadCount()>1) {
            Map<String, List<NER_Candidate>> solved = extractor.getResults();
            if(solved != null) {
                for(String weightMethod:solved.keySet()) {
                    List<NER_Candidate> get = weightMethod_Candidate.get(weightMethod);
                    if(get != null && solved.get(weightMethod) != null) {
                        get = solved.get(weightMethod);
                        weightMethod_Candidate.put(weightMethod, get);
                    }
                    else {
                        get.addAll(solved.get(weightMethod));
                    }
                }
            }
        }
        return WorkSpace.getEvaluations(weightMethod_Candidate, kbTrie.getEntityList());
    }

}
